#include "url.h"
#include <QStack>
#include <QDebug>

//QRegExp Url::m_urlRx = QRegExp("([^.]+|[{\\[\\(]{1}([^{}\\(\\)\\[\\]]*)[}\\]\\)]{1})");
QRegExp Url::m_argsRx = QRegExp("([^, ]+|[\\[\"']{1}([^\\[\\]\"']*)[\\]\"'']{1})");


Url::Url() :
    m_curNode(0)
{}

Url::Url(const QString &urlString)
{
    m_curNode = 0;

    // argument(s)
    int n = urlString.indexOf('=');
    if (n == -1) {
        n = urlString.indexOf('(');
        if (n == -1) {
            n = urlString.length();
        } else {
            m_argumentData = parse_args(urlString
                                        .mid(n + 1, urlString.length() - n - 2));
            m_argumentType = FUNCTION;
        }
    } else {
        m_argumentData = urlString.right(urlString.length() - n - 1);
        m_argumentType = PROPERTY;
    }


    foreach (QString nodeStr, urlString.left(n).split('/', QString::SkipEmptyParts))
        m_nodes.append(UrlNode(nodeStr));
// parse with regexp
//    QRegExp rx = m_urlRx;
//    m_nodes.clear();
//    m_curNode = 0;
//    int pos = 0;
//    while((pos = urlString.indexOf(rx, pos)) >= 0) {
//        pos += rx.cap(0).length();
//        qDebug() << "CAP:" << rx.cap(0);
//        m_nodes.append(UrlNode(rx.cap(0)));
}

Url::Url(const QVector<UrlNode> &nodes, Url::ArgumentType argumentType, const QVariant &argumentData, int currentNodeIdx)
    : m_nodes(nodes), m_curNode(currentNodeIdx),
      m_argumentData(argumentData), m_argumentType(argumentType)
{}

bool Url::isValid() const
{
    return !m_nodes.isEmpty();
}

bool Url::isTargetNet() const
{
    return m_curNode == m_nodes.count() - 2;
}

bool Url::next() const
{
    if (m_curNode == m_nodes.count() - 1)
        return false;
    m_curNode++;
    return true;
}

UrlNode Url::currentNode() const
{
    if (m_nodes.isEmpty() || m_curNode >= m_nodes.count())
        return UrlNode();
    return m_nodes.at(m_curNode);
}

int Url::currentNodeIdx() const
{
    return m_curNode;
}

UrlNode Url::node(int index) const
{
    if (index >= 0 && index < m_nodes.count())
        return m_nodes[index];
    return UrlNode();
}

int Url::nodesCount() const
{
    return m_nodes.count();
}

QVector<UrlNode> Url::nodes() const
{
    return m_nodes;
}

void Url::prepend(UrlNode node)
{
    m_nodes.push_front(node);
}

void Url::append(UrlNode node)
{
    m_nodes.push_back(node);
}

bool Url::match(const Url &other, int untillNode) const
{
    if (untillNode == -1)
        untillNode = m_nodes.count() - 1;
    if ((untillNode + 1) != other.nodesCount())
        return false;
    for (int i = 0; i <= untillNode; ++i)
        if (!UrlNode::match(m_nodes[i], other.node(i)))
            return false;
    return true;
}

QString Url::toString() const
{
    QString result;
    for (int i = 0; i < m_nodes.count(); ++i) {
        if (i == m_curNode)
            result += "|*|";
        result += m_nodes[i].toString();

        if (i != m_nodes.count() - 1)
            result += " -> ";
    }

    switch (m_argumentType) {
    case PROPERTY: {
        result += " property";
        result += " val:" + m_argumentData.toString();
        break;
    }
    case FUNCTION: {
        result += " function:";
        result += " args:";
        QVariantList l = m_argumentData.toList();
        for (int i = 0; i < l.count(); ++i) {
            result += l[i].toString();
            if (i != l.count() - 1) result += ',';
        }
    }
    }

    return result;
}

Url::ArgumentType Url::argumentType() const
{
    return m_argumentType;
}

QVariant Url::argumentData() const
{
    return m_argumentData;
}

void Url::setArgumentType(Url::ArgumentType type)
{
    m_argumentType = type;
}

void Url::setArgumentData(const QVariant &data)
{
    m_argumentData = data;
}

QDataStream &operator <<(QDataStream &out, const Url &url)
{
    QVector<UrlNode> nodes = url.nodes();
    qint32 count = (qint32)nodes.count();
    out << count << (qint32)url.currentNodeIdx();
    for (int i = 0; i < count; ++i)
        out << nodes[i];
    out << (quint8)url.argumentType() << url.argumentData();
    return out;
}

QDataStream &operator >>(QDataStream &in, Url &url)
{
    qint32 count, currentNodeIdx;
    in >> count >> currentNodeIdx;
    QVector<UrlNode> nodes;
    nodes.reserve(count);
    for (int i = 0; i < count; ++i) {
        UrlNode node;
        in >> node;
        nodes.push_back(node);
    }
    quint8 argumentType;
    QVariant argumentData;
    in >> argumentType >> argumentData;
    url = Url(nodes, (Url::ArgumentType)argumentType, argumentData, currentNodeIdx);
    return in;
}

QVariantList Url::parse_args(const QString &args) const
{
    QRegExp rx = m_argsRx;
    int pos = 0;
    QVariantList vl;
    while((pos = args.indexOf(rx, pos)) >= 0) {
        pos += rx.cap(0).length();
        QString a = rx.cap(0);
//       qDebug() << a;
        if (a[0] == '[') {
            vl.append(parse_args(a.mid(1, a.length() - 2)));
        } else if (a[0] == '\'' || a[0] == '"') {
            vl.append(a.mid(1, a.length() - 2));
        } else {
            vl.append(a);
        }
    }
    return vl;
}
